{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# **Ноутбук (блокнот) создан на основе этой статьи:**\n",
        " https://xakep.ru/2019/08/29/nn-digits-recognition/\n",
        "\n",
        " Подробнее о том, как работать в Google Colab можно почитать например здесь:\n",
        " https://skillbox.ru/media/code/data_science_dostupnyy_kazhdomu/"
      ],
      "metadata": {
        "id": "idNZtIX9orIZ"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Еще раз повторим как работает нейронная сеть.\n",
        "\n",
        "Как работает один нейрон? Сигналы со входов (1) суммируются (2), причем каждый вход имеет свой «коэффициент передачи» — w. Затем к получившемуся результату применяется «функция активации» (3).\n",
        "![neural01.png]()"
      ],
      "metadata": {
        "id": "WM799N-ypFdK"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Типы этой функции различны, она может быть:\n",
        "\n",
        " **прямоугольной (на выходе 0 или 1)**;\n",
        "\n",
        " **линейной;**\n",
        "\n",
        " **в виде сигмоиды.**\n",
        "\n",
        "Еще в 1943 году Мак-Каллок и Питтс доказали, что сеть из нейронов может выполнять различные операции. Но сначала эту сеть нужно обучить — настроить коэффициенты w каждого нейрона так, чтобы сигнал передавался нужным нам способом. Запрограммировать нейронную сеть и обучить ее с нуля сложно, но, к счастью для нас, все необходимые библиотеки уже написаны. Благодаря компактности языка Python все действия можно запрограммировать в несколько строк кода."
      ],
      "metadata": {
        "id": "qIIJ0z2xp3AZ"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# **Рассмотрим простейшую нейросеть и научим ее выполнять функцию XOR**.\n",
        "# Как работает функция XOR (исключающее ИЛИ):\n",
        "\n",
        "![Безымянный.png]()\n"
      ],
      "metadata": {
        "id": "LQF6Mc2DSQzE"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4IX5RReDE4nb"
      },
      "outputs": [],
      "source": [
        "# Для запуска ноутбука потребуется библиотека Tensorflow (здесь  ней подробнее:\n",
        "# https://skillbox.ru/media/code/biblioteka-tensorflow-pishem-neyroset-i-izuchaem-printsipy-mashinnogo-obucheniya/)\n",
        "\n",
        "!pip install tensorflow"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Подключаем другие необходимые модули:\n",
        "from tensorflow import keras\n",
        "from keras.models import Sequential\n",
        "from keras.layers import Dense\n",
        "import numpy as np"
      ],
      "metadata": {
        "id": "xwpE_fV_Gv7Z"
      },
      "execution_count": 2,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# Теперь мы готовы создать нейросеть. Благодаря Tensorflow на это понадобится всего лишь четыре строчки кода.\n",
        "model = Sequential()\n",
        "model.add(Dense(2, input_dim=2, activation='relu')) # функция активации - 'relu'\n",
        "model.add(Dense(1, activation='sigmoid')) # функция активации - 'sigmoid'\n",
        "model.compile(loss='binary_crossentropy', optimizer='adam', metrics='accuracy')"
      ],
      "metadata": {
        "id": "FK0LEUgUG6Ek"
      },
      "execution_count": 3,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Разберём код подробнее. **Sequential** — это тип нейросети, означающий, что процесс обучения будет последовательным. Это стандартный процесс обучения для простых нейросетей: в нём она сначала делает предсказания, затем тестирует их и сравнивает с результатом, а в конце — корректирует ошибки. Dense — это в данном случае тип слоя.\n",
        "Мы создали модель нейронной сети и добавили в нее слои: входной, скрытый и выходной. Такая сеть называется «многослойный перцептрон» (multilayer perceptron), в общем виде она выглядит так:\n",
        "\n",
        "![nn1.png]()\n",
        "\n",
        "В нашем случае сеть имеет два входа (внешний слой), два нейрона во внутреннем слое и один выход.\n",
        "**Можно посмотреть, что у нас получилось:**"
      ],
      "metadata": {
        "id": "Bu09ExYorEaq"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "print(model.summary())"
      ],
      "metadata": {
        "id": "C4U52Q_3HT0u"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Обучение нейросети состоит в нахождении значений параметров этой сети.\n",
        "Наша сеть имеет девять параметров. Чтобы обучить ее, нам понадобится исходный набор данных, в нашем случае это результаты работы функции XOR. Обучение займет какое-то время."
      ],
      "metadata": {
        "id": "fljv7im9su5V"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Наши входные данные a и b - см. таблицу выше (где показаны результаты работы функции XOR).\n",
        "X = np.array([[0,0], [0,1], [1,0], [1,1]])\n",
        "\n",
        "# Выходные данные, которые мы долны предсказать (т.е. 0 и 0 на входе дают 0 на выходе и т.п.):\n",
        "y = np.array([[0], [1], [1], [0]])\n",
        "\n",
        "# Обучаем нашу нейросеть, используем 2000 эпох обучения:\n",
        "model.fit(X, y, batch_size=1, epochs=2000, verbose=0)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "hLaBEHCAKdG6",
        "outputId": "4bbeb250-b8ef-4d90-aa96-a714df02afef"
      },
      "execution_count": 5,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<keras.src.callbacks.History at 0x7e60f9e5cfa0>"
            ]
          },
          "metadata": {},
          "execution_count": 5
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Можем посмотреть, какие получились веса и смещения после обучения.\n",
        "## Parameters layer 1\n",
        "W1 = model.get_weights()[0]\n",
        "b1 = model.get_weights()[1]\n",
        "## Parameters layer 2\n",
        "W2 = model.get_weights()[2]\n",
        "b2 = model.get_weights()[3]\n",
        "\n",
        "print(\"W1:\", W1)\n",
        "print(\"b1:\", b1)\n",
        "print(\"W2:\", W2)\n",
        "print(\"b2:\", b2)\n",
        "print()"
      ],
      "metadata": {
        "id": "nYX9OZhXKjuw",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "cc99249d-0635-4f34-a61a-92f31272c7e8"
      },
      "execution_count": 6,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "W1: [[ 1.5589707 -2.05749  ]\n",
            " [-1.5622256  2.0555444]]\n",
            "b1: [-0.00235292 -0.00040028]\n",
            "W2: [[4.418547 ]\n",
            " [3.4081564]]\n",
            "b2: [-2.5067573]\n",
            "\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Но алгоритм обучения, который используется в **Keras**, не идеален: нейросети не всегда удается обучиться за 2000 итераций, и результаты не всегда верны. Так, Keras инициализирует начальные значения случайными величинами, и при каждом запуске результат может отличаться. Моя сеть с двумя нейронами успешно обучалась лишь в 20% случаев. Неправильная работа сети выглядит примерно так:\n",
        "\n",
        "XOR(0,0): [[0.66549516]]\n",
        "\n",
        "XOR(0,1): [[0.66549516]]\n",
        "\n",
        "XOR(1,0): [[0.66549516]]\n",
        "\n",
        "XOR(1,1): [[0.00174837]]\n",
        "\n",
        "Но это не страшно. Если видишь, что нейронная сеть во время обучения не выдает правильных результатов, алгоритм обучения можно запустить еще раз. Правильно обученную сеть потом можно использовать без ограничений."
      ],
      "metadata": {
        "id": "NAHXNDqjthzh"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Посмотрим на результаты работы нашей нейронной сети:\n",
        "\n",
        "print(\"Network test:\")\n",
        "print(\"XOR(0,0):\", model.predict(np.array([[0, 0]])))\n",
        "print(\"XOR(0,1):\", model.predict(np.array([[0, 1]])))\n",
        "print(\"XOR(1,0):\", model.predict(np.array([[1, 0]])))\n",
        "print(\"XOR(1,1):\", model.predict(np.array([[1, 1]])))"
      ],
      "metadata": {
        "id": "xNKlG6cFKkLz"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Можно сделать сеть поумнее: использовать четыре нейрона вместо двух, для этого достаточно заменить строчку кода\n",
        "\n",
        "*model.add(Dense(**2**, input_dim=2, activation='relu'))*\n",
        "\n",
        "на\n",
        "\n",
        "*model.add(Dense(**4**, input_dim=2, activation='relu'))*\n",
        "\n",
        "такая сеть обучается уже в 60% случаев, а сеть с шестью нейронами - в 90% случаев."
      ],
      "metadata": {
        "id": "p4GzfV1FuDZa"
      }
    }
  ]
}